package edu.ucdavis.gwt.gis.client.toolbar;

import java.util.LinkedList;

import com.google.gwt.core.client.JsArray;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.BlurEvent;
import com.google.gwt.event.dom.client.BlurHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
import com.google.gwt.event.dom.client.MouseOverHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;

import edu.ucdavis.cstars.client.Error;
import edu.ucdavis.cstars.client.Graphic;
import edu.ucdavis.cstars.client.InfoTemplate;
import edu.ucdavis.cstars.client.MapWidget;
import edu.ucdavis.cstars.client.Util;
import edu.ucdavis.cstars.client.Graphic.Attributes;
import edu.ucdavis.cstars.client.callback.AddressToLocationsCallback;
import edu.ucdavis.cstars.client.callback.LocationToAddressCallback;
import edu.ucdavis.cstars.client.dojo.Color;
import edu.ucdavis.cstars.client.geometry.Geometry;
import edu.ucdavis.cstars.client.geometry.Point;
import edu.ucdavis.cstars.client.layers.GraphicsLayer;
import edu.ucdavis.cstars.client.symbol.SimpleLineSymbol;
import edu.ucdavis.cstars.client.symbol.SimpleMarkerSymbol;
import edu.ucdavis.cstars.client.tasks.Address;
import edu.ucdavis.cstars.client.tasks.AddressCandidate;
import edu.ucdavis.cstars.client.tasks.Locator;
import edu.ucdavis.gwt.gis.client.Debugger;
import edu.ucdavis.gwt.gis.client.extras.CloseButton;

public class EsriSearchPanel extends Composite {
	

	private Locator locator = Locator.create("http://tasks.arcgis.com/ArcGIS/rest/services/WorldLocator/GeocodeServer");
	
	private GraphicsLayer graphicsLayer;
	private Graphic marker = null;
	
	private VerticalPanel panel = new VerticalPanel();
	private GeocodeResultsPanel results = new GeocodeResultsPanel(this);
	private TextBox tb = new TextBox();
		
	private LinkedList<AddressCandidate> locs = null;
	
	private AddressCandidate currentPlace = null;
	
	private MapWidget map = null;
	
	public EsriSearchPanel() {
		
		results.setWidth("100%");
		panel.setVerticalAlignment(HorizontalPanel.ALIGN_MIDDLE);
		
		HorizontalPanel hp = new HorizontalPanel();
		hp.setVerticalAlignment(HorizontalPanel.ALIGN_MIDDLE);
		hp.add(new HTML("<div style='padding-right: 2px; white-space:nowrap; #444444'>Find Location&nbsp; </div>"));
		tb.setStyleName("searchBoxInput");
		hp.add(tb);
		
		tb.addBlurHandler(new BlurHandler(){
			public void onBlur(BlurEvent event) {
				if( results.getResultCount() <= 1){
					results.hide();
				}
			}
		});
		
		panel.add(hp);

		
		tb.addKeyUpHandler(new KeyUpHandler(){
			public void onKeyUp(KeyUpEvent event) {
				if( event.getNativeKeyCode() == KeyCodes.KEY_ENTER ){
					//tb.setFocus(false);
					String searchTxt = tb.getText().toLowerCase();
					results.clear();
					
					if( searchTxt.contentEquals("debug") ) {
						Debugger.INSTANCE.show();
						tb.setText("");
						return;
					}
					
					if( searchTxt.matches("-?\\d+.?\\d*\\s*,\\s*-?\\d+.?\\d*") ){
						String[] parts = searchTxt.split(",");
						try {
							double lat = Double.parseDouble(parts[0].replaceAll("\\s", ""));
							double lng = Double.parseDouble(parts[1].replaceAll("\\s", ""));
							if( (lat < -117 && lat > -125) && (lng > 30 && lng < 43)) { // switch lat/lng
								double tmp = lat;
								lat = lng;
								lng = tmp;
							}
							Point p = Point.create((float) lat, (float)lng, map.getSpatialReference());
							locator.locationToAddress(p, 1000, new LocationToAddressCallback(){
								@Override
								public void onError(Error error) {
									results.add(new HTML("No Reverse Geocoding Results"));
									results.show();
								}
								@Override
								public void onLocationToAddressComplete(
										AddressCandidate candidate) {
									onGeoSuccess(candidate);
								}
							});
						} catch(Exception e) {
							results.add(new HTML("Looks like you are trying to reverse geocode<br />but error in parse of code"));
							results.show();
						}
					} else {
						
						Address addr = Address.create();
						
						addr.setSingleLineInput(searchTxt);
						
						Locator.Parameters params = Locator.Parameters.create();
						params.setAddress(addr);
						params.setOutFields(new String[] {"*"});
						
						locator.addressToLocations(params, new AddressToLocationsCallback(){
							@Override
							public void onAddressToLocationsComplete(
									JsArray<AddressCandidate> candidates) {
								results.clear();
								if( candidates.length() == 0 ) {
									results.add(new HTML("No Geocoding Results"));
									results.show();
								} else {
									onGeoSuccess(candidates);
								}
							}
							@Override
							public void onError(Error error) {
								results.clear();
								results.add(new HTML("No Geocoding Results"));
								results.show();
							}
						});
					}
				}
			}
		});
		
		//panel.add(results);
		
		//SimplePanel scp = new SimplePanel();
		panel.getElement().getStyle().setPadding(5, Unit.PX);
		//scp.add(panel);
		
		initWidget(panel);
	}

	protected void setMarker(AddressCandidate place){
		currentPlace = place;
		if( marker != null ) graphicsLayer.remove(marker);
		
		Point p = place.getLocation();
		p = (Point) Geometry.geographicToWebMercator(p);
		
		String addrString = place.getAddressAsString();
		
		InfoTemplate it = InfoTemplate.create("Location: <b>"+currentPlace.getLocation().toNiceString(),
				"<b>Address: </b>"+addrString);
		SimpleMarkerSymbol symbol = SimpleMarkerSymbol.create(
				SimpleMarkerSymbol.StyleType.STYLE_DIAMOND, 
				14, 
				SimpleLineSymbol.create(
						SimpleLineSymbol.StyleType.STYLE_SOLID,
						Color.create(0, 0, 0, 0.7),
						1),
				Color.create(255, 0, 0, 0.7)
		);
		marker = Graphic.create(p, symbol, Attributes.create(), it);
		
		graphicsLayer.add(marker);
		tb.setText(addrString);
		map.centerAt(p);
	
	}
	
	protected void onGeoSuccess(AddressCandidate location) {
		setMarker(location);
	}
	
	protected void onGeoSuccess(JsArray<AddressCandidate> locations) {
		if( locations.length() == 1 ){
			setMarker(locations.get(0));
		} else if ( locations.length() > 1 ){
			locs = getTopLocations(locations);
			HorizontalPanel hp = new HorizontalPanel();
			hp.setWidth("100%");
			hp.add(new HTML("Did you mean: "));
			CloseButton close = new CloseButton();
			close.addClickHandler(new ClickHandler(){
				public void onClick(ClickEvent event) {
					results.hide();
				}
			});
			hp.add(close);
			hp.setCellHorizontalAlignment(close, HorizontalPanel.ALIGN_RIGHT);
			results.addResult(hp);
			for( int i = 0; i < locs.size(); i++ ){					
				Label l = new Label(locs.get(i).getAddressAsString());
				l.getElement().setId("locSearch-"+i);
				l.addMouseOverHandler(new MouseOverHandler(){
					public void onMouseOver(MouseOverEvent event) {
						Label l = (Label) event.getSource();
						l.addStyleDependentName("mouseover");				
					}
				});
				l.addMouseOutHandler(new MouseOutHandler(){
					public void onMouseOut(MouseOutEvent event) {
						Label l = (Label) event.getSource();
						l.removeStyleDependentName("mouseover");				
					}
				});
				l.setStyleName("LabelButton");
				l.addClickHandler(new ClickHandler(){
					public void onClick(ClickEvent event) {
						Label l = (Label) event.getSource();
						int id = Integer.parseInt(l.getElement().getId().replaceAll("locSearch-",	""));
						setMarker(locs.get(id));
						results.clear();
						results.hide();
					}
				});
				l.getElement().getStyle().setProperty("borderTop", "1px solid #cccccc");
				results.addResult(l);
			}
			results.show();
		}
	}


	public void init(MapWidget mapWidget) {		
		map = mapWidget;
		graphicsLayer = map.getGraphics();
	}
	
	public void test() {
		Point p = map.getExtent().getCenter();
		Window.alert(p.getX()+","+p.getY()+" - "+p.getSpatialReference().getWkid());
		InfoTemplate it = InfoTemplate.create("This is a test","This is only a test");
		SimpleMarkerSymbol symbol = SimpleMarkerSymbol.create(
				SimpleMarkerSymbol.StyleType.STYLE_DIAMOND, 
				14, 
				SimpleLineSymbol.create(
						SimpleLineSymbol.StyleType.STYLE_SOLID,
						Color.create(0, 0, 0, 1),
						1),
				Color.create(255, 0, 0, 0.7)
		);
		marker = Graphic.create(p, symbol, Attributes.create(), it);
		
		graphicsLayer.add(marker);
	}
	
	private LinkedList<AddressCandidate> getTopLocations(JsArray<AddressCandidate> list ){
		LinkedList<AddressCandidate> tmpList = new LinkedList<AddressCandidate>();
		LinkedList<AddressCandidate> top = new LinkedList<AddressCandidate>();
		
		// remove all duplicates, but keep the highest score
		for( int i = 0; i < list.length(); i++ ){
			AddressCandidate addr = list.get(i);
			boolean found = false;
			for( int j = 0; j < tmpList.size(); j++ ){
				if( addr.getAddressAsString().contentEquals(tmpList.get(j).getAddressAsString()) ){
					if( addr.getScore() > tmpList.get(j).getScore() ){
						tmpList.remove(j);
						tmpList.add(j, addr);
					}
					found = true;
					break;
				}
			}
			if( !found ) tmpList.add(addr);
		}
		
		// now grab the top 5 scores
		for( int i = 0; i < tmpList.size(); i++ ){
			AddressCandidate addr = tmpList.get(i);
			boolean found = false;
			for( int j = 0; j < top.size(); j++ ){
				if( addr.getScore() > top.get(j).getScore() ){
					top.add(j, addr);
					found = true;
					break;
				}
			}
			if( !found && top.size() <= 5 ) top.add(addr);
			if( top.size() > 5 ) top.removeLast();
		}
		
		return top;
	}

	
	/*
	private String getStringAddress(AddressCandidate place){
		 return place.getAddress().getStreet()+"<br />"+place.getAddress().getCity()
			+", "+place.getAddress().getState()+" "+place.getAddress().getZone();
	}
	*/
}
